#ifndef BALL_H
#define BALL_H

class Ball;

#include "PlayState.h"
#include "Constants.h"
#include "Player.h"

#include <vector>

typedef enum {
    atTELEPORT_IN,
    atTELEPORT_OUT,
    atGOAL_IN,
    atSIZE_INCREASE,
    atSIZE_DECREASE
} BallAnimType;

/*
 * Class for controlling the ball
 */
class Ball : public OgreNewt::ContactCallback, public Ogre::FrameListener
{
private:
    Player *player; // pointer to parent

    OgreNewt::Body *body;
    const OgreNewt::World *ogre_newt;
    const OgreNewt::Collision *original_collision;
    Ogre::SceneNode *ogre_node;
    PlayState *play_state;
    
    Ogre::Vector3 initial_direction;
    
    bool apply_force;
    Ogre::Vector3 gravity;
    Ogre::Vector3 gravity_direction;
    Ogre::Real mass, default_mass;
    Ogre::Vector3 default_size;
    Ogre::Real size_factor;
    Ogre::Vector3 size;
    
    Ogre::Vector3 teleport_destination, teleport_direction;
        
    Ogre::ParticleEmitter *col_particles;
    Ogre::SceneNode *col_particles_node;
    Ogre::Real last_collision_time;
    
    // special callbacks for applying drag force to bodies and when velocity has changed.    
    void gravityCallback( OgreNewt::Body* me );
    
    void clearForces();
    void refreshSize();
    
    float previous_collision_time, total_time, previous_speed;
    float last_floor_contact_time, last_downward_simulation_time;
    Ogre::Vector3 previous_direction, previous_omega;
    bool teleporting;
    bool in_contact;
    
    bool animating;
    BallAnimType anim_type;
    int anim_state;
    Ogre::Real anim_time;
    
    void startAnimation(BallAnimType t);
    void resetAnimation();
    void animateTeleportIn();
    void doTeleport();
    void animateTeleportOut();
    void animateGoalIn();
    
    Ogre::SceneNode *tele_particles_node;
    Ogre::ParticleSystem *tele_exp_particles;
    Ogre::ParticleSystem *tele_flash_particles;
    
    Ogre::SceneNode *goal_particles_node;
    Ogre::ParticleSystem *goal_exp_particles;
    Ogre::ParticleSystem *goal_flash_particles;
    
    bool visible;

public:
    Ball(OgreNewt::Body *b);
   ~Ball(void);
   
    void setPlayer(Player *pl) { player = pl; }
    Player *getPlayer() { return player; }
    
    Ogre::Real getMass() { return mass; }
    OgreNewt::Body *getOgreNewtBody() { return body; }
    float getScale() { return size_factor; }
    void setGravity(Ogre::Vector3 g);
    Ogre::Vector3 getGravity() { return teleporting ? Ogre::Vector3::NEGATIVE_UNIT_Y * Constants::GRAVITY_STRENGTH : gravity; }
    Ogre::Vector3 getGravityDirection() { return gravity_direction; }
    Ogre::Vector3 getVelocity() { return body->getVelocity(); }
    void setPosition(Ogre::Vector3 position);
    Ogre::Vector3 getPosition() { return ogre_node->getPosition(); }
    void increaseSize();
    void decreaseSize();
	void reset();
	void teleport(Ogre::Vector3 destination, Ogre::Vector3 direction);
    void finishedTeleporting();
    void restartAfterTeleport();
    Ogre::Vector3 getInitialDirection() { return initial_direction; }
	
	Ogre::SceneNode *getSceneNode() { return ogre_node; }
    
    void setPlayState(PlayState *ps);
    Ball *clone(Ogre::SceneManager *scene_mgr, Ogre::String suffix, int new_player_id);
    void predictCollisions(float time);
    
    void collision( Ogre::Vector3 &pos, Ogre::Vector3 &norm);
    void goalCollision();
    
    bool frameStarted(Ogre::Real time);
    
    void setVisibility(bool visible);
} ;

#endif
